package com.ccteam.fluidmusic.ui.local

import android.app.Application
import android.net.Uri
import android.os.Bundle
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaMetadataCompat
import androidx.core.os.bundleOf
import androidx.lifecycle.*
import com.ccteam.fluidmusic.R
import com.ccteam.fluidmusic.fluidmusic.common.MusicServiceConnectionFlow
import com.ccteam.fluidmusic.fluidmusic.common.utils.MediaIDHelper
import com.ccteam.fluidmusic.fluidmusic.media.extensions.METADATA_KEY_ALBUM_ID
import com.ccteam.fluidmusic.fluidmusic.media.extensions.METADATA_KEY_ARTIST_ID
import com.ccteam.fluidmusic.fluidmusic.media.extensions.id
import com.ccteam.fluidmusic.utils.cancelIfActive
import com.ccteam.fluidmusic.utils.setValueIfNew
import com.ccteam.fluidmusic.view.bean.ErrorMessage
import com.ccteam.fluidmusic.view.bean.LoadMessage
import com.ccteam.model.Resource
import com.ccteam.shared.result.BaseItemData
import com.ccteam.shared.result.album.AlbumListItem
import com.ccteam.shared.result.artist.ArtistInfoData
import com.ccteam.shared.result.artist.ArtistListItem
import com.ccteam.shared.result.music.DefaultMusicItem
import com.ccteam.shared.utils.NO_PLAYING
import com.ccteam.shared.utils.PLAYING
import com.ccteam.shared.utils.getPlaybackStatus
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject

/**
 *
 * @author Xiaoc
 * @since 2020/12/30
 *
 * 本地音乐页面ViewModel
 * 该ViewModel进行调用本地数据，得到需要订阅的本地ID数据
 */
@HiltViewModel
class LocalMusicViewModel @Inject constructor(
    private val musicServiceConnectionFlow: MusicServiceConnectionFlow,
    application: Application
): AndroidViewModel(application) {

    /**
     * 重新扫描的对于子项的状态
     * 重新扫描的状态有三种
     * [RESCAN_NONE] 不是处于扫描状态，此时子项可以订阅对应内容
     * [RESCAN_LOADING] 正在扫描中
     * [RESCAN_FINISH] 扫描完成，防止子项进行重新订阅
     */
    private val _rescanStatusByTab = MutableLiveData<MutableMap<String,Int>>(mutableMapOf())
    val rescanStatusByTab: LiveData<MutableMap<String,Int>> = _rescanStatusByTab

    /**
     * 所有音乐的信息数据
     */
    private val _musicItems = MutableLiveData<Resource<List<DefaultMusicItem>>>(Resource.Loading(null))
    val musicItems: LiveData<Resource<List<DefaultMusicItem>>> get() =  _musicItems

    val musicLoadMessage: LiveData<LoadMessage> = _musicItems.map {
        when (it) {
            is Resource.Success -> {
                if(it.data.isNullOrEmpty()){
                    LoadMessage.Empty
                } else {
                    LoadMessage.NotLoading
                }
            }
            is Resource.Loading -> {
                LoadMessage.Loading
            }
            else -> {
                LoadMessage.Error(ErrorMessage(it.message,it.errorCode))
            }
        }
    }

    /**
     * 所有专辑的信息数据
     */
    private val _albumItems = MutableLiveData<Resource<List<AlbumListItem>>>(Resource.Loading(null))
    val albumItems: LiveData<Resource<List<AlbumListItem>>> get() =  _albumItems

    val albumLoadMessage: LiveData<LoadMessage> = _albumItems.map {
        when (it) {
            is Resource.Success -> {
                if(it.data.isNullOrEmpty()){
                    LoadMessage.Empty
                } else {
                    LoadMessage.NotLoading
                }
            }
            is Resource.Loading -> {
                LoadMessage.Loading
            }
            else -> {
                LoadMessage.Error(ErrorMessage(it.message,it.errorCode))
            }
        }
    }

    /**
     * 所有歌手的信息数据
     */
    private val _artistItems = MutableLiveData<Resource<List<ArtistListItem>>>(Resource.Loading(null))
    val artistItems: LiveData<Resource<List<ArtistListItem>>> get() =  _artistItems

    val artistLoadMessage: LiveData<LoadMessage> = _artistItems.map {
        when (it) {
            is Resource.Success -> {
                if(it.data.isNullOrEmpty()){
                    LoadMessage.Empty
                } else {
                    LoadMessage.NotLoading
                }
            }
            is Resource.Loading -> {
                LoadMessage.Loading
            }
            else -> {
                LoadMessage.Error(ErrorMessage(it.message,it.errorCode))
            }
        }
    }

    /**
     * Local本地音乐的根ID
     */
    val parentId = MutableLiveData<String>()

    /**
     * 本地订阅根目录后得到的内容
     * 其内容是Tab栏显示的内容
     */
    private val _localParentIds = MediatorLiveData<List<Pair<String,String>>>()
    val localParentIds: LiveData<List<Pair<String,String>>> = _localParentIds

    /**
     * 未知年份字符
     */
    private val unknownYear: String = application.getString(R.string.description_unknown_year)

    private var musicItemsChangedJob: Job? = null

    init {
        musicItemsChangedJob = viewModelScope.launch(Dispatchers.Default) {
            musicServiceConnectionFlow.nowPlaying.collectLatest {
                _musicItems.postValue(handleMusicItemsChanged(mediaMetadata = it))
            }
        }

        _localParentIds.addSource(parentId){
            subscribeLocalRootId(false)
        }
    }

    fun setParentId(newParentId: String){
        parentId.setValueIfNew(newParentId)
    }

    /**
     * 订阅回调处理
     */
    private val rootSubscriptionCallback = object : MediaBrowserCompat.SubscriptionCallback() {
        override fun onChildrenLoaded(parentId: String, children: List<MediaBrowserCompat.MediaItem>) {
            _localParentIds.value = children.map {
                Pair(it.mediaId!!,it.description.title.toString())
            }
            // 将所有子项订阅状态设置为未扫描，让子项去进行订阅操作
            _rescanStatusByTab.value = children.map {
                Pair(it.mediaId!!, RESCAN_NONE)
            }.toMap().toMutableMap()
        }

        /**
         * 重新扫描会走这个回调（因为传了Bundle）
         */
        override fun onChildrenLoaded(
            parentId: String,
            children: MutableList<MediaBrowserCompat.MediaItem>,
            options: Bundle
        ) {
            // 将所有子项订阅状态设置为未扫描，让子项去进行订阅操作
            _rescanStatusByTab.value = children.map {
                Pair(it.mediaId!!, RESCAN_NONE)
            }.toMap().toMutableMap()
        }

    }

    /**
     * 使用协程处理从Service回调的数据
     * 该回调方法处理了三种不同的数据（所有音乐、专辑分类和歌手分类）
     */
    private val multiSubscriptionCallback = object : MediaBrowserCompat.SubscriptionCallback() {
        override fun onChildrenLoaded(parentId: String, children: List<MediaBrowserCompat.MediaItem>) {
            viewModelScope.launch {
                when(parentId){
                    MediaIDHelper.LOCAL_MEDIA_ALL ->{
                        handleMusicItems(children)
                    }
                    MediaIDHelper.LOCAL_MEDIA_ALBUM ->{
                        handleAlbumItems(children)
                    }
                    MediaIDHelper.LOCAL_MEDIA_ARTIST ->{
                        handleArtistItems(children)
                    }
                }
                // 将重新扫描的状态置为扫描完成，代表当前内容已经被订阅，防止重复订阅
                _rescanStatusByTab.value?.put(parentId, RESCAN_FINISH)
                _rescanStatusByTab.value = _rescanStatusByTab.value
            }
        }
    }

    /**
     * 订阅本地RootId下的目录结构
     * 调用后会进行根目录订阅，并告诉服务是否需要重新扫描媒体库
     * 然后会回调 [MediaBrowserCompat.SubscriptionCallback.onChildrenLoaded] 方法
     *
     * @param rescan 是否是重新扫描的命令
     */
    fun subscribeLocalRootId(rescan: Boolean = false){
        if(parentId.value.isNullOrEmpty()){
            return
        }
        if(rescan){
            // 如果是重新扫描，先取消一次订阅，再进行重新订阅
            musicServiceConnectionFlow.unsubscribe(parentId.value!!,rootSubscriptionCallback)

            val bundle = bundleOf(
                MediaIDHelper.OPTION_LOCAL_MEDIA_RESCAN to MediaIDHelper.OPTION_LOCAL_MEDIA_RESCAN
            )
            musicServiceConnectionFlow.subscribe(parentId.value!!,rootSubscriptionCallback,bundle)

        } else {
            // 如果不是重新扫描的订阅，说明是第一次订阅，则进行最近本的初始化工作
            musicServiceConnectionFlow.subscribe(parentId.value!!,rootSubscriptionCallback)
        }
    }

    /**
     * 订阅根ID结构下的子项内容
     */
    fun subscribeByChildrenParentId(childrenParentId: String){
        _rescanStatusByTab.value?.get(childrenParentId)?.let {
            // 先取消一次当前订阅，则需要取消一次订阅
            musicServiceConnectionFlow.unsubscribe(childrenParentId,multiSubscriptionCallback)
            musicServiceConnectionFlow.subscribe(childrenParentId,multiSubscriptionCallback)
        }
    }


    override fun onCleared() {
        super.onCleared()

        parentId.value?.let {
            musicServiceConnectionFlow.unsubscribe(it,rootSubscriptionCallback)
        }
        // 取消子类所有订阅
        _rescanStatusByTab.value?.forEach {
            musicServiceConnectionFlow.unsubscribe(it.key,multiSubscriptionCallback)
        }

        musicItemsChangedJob.cancelIfActive()

    }

    /**
     * 使用协程处理回调数据（所有音乐内容）
     */
    private suspend fun handleMusicItems(children:List<MediaBrowserCompat.MediaItem>){
        withContext(Dispatchers.Default){
            val itemsList = children.map { child ->
                val artistInfo = child.description.extras?.getString(METADATA_KEY_ARTIST_ID)?.let {
                    listOf(ArtistInfoData(it,child.description.subtitle.toString()))
                } ?: emptyList()
                DefaultMusicItem(
                    child.mediaId!!,
                    child.description.title.toString(),
                    child.description.subtitle.toString() + " · " + child.description.description,
                    artistInfo,
                    child.description.extras?.getString(METADATA_KEY_ALBUM_ID) ?: "-1",
                    child.description.iconUri.toString(),
                    null,
                    child.isPlayable,
                    musicServiceConnectionFlow.nowPlaying.value.getPlaybackStatus(child.mediaId!!),
                    child.description.mediaUri ?: Uri.EMPTY
                )
            }
            _musicItems.postValue(Resource.Success(itemsList))
        }
    }

    /**
     * 使用协程处理回调的数据（所有专辑内容）
     */
    private suspend fun handleAlbumItems(children:List<MediaBrowserCompat.MediaItem>){
        withContext(Dispatchers.Default){
            val itemsList = children.map { child ->
                // 获得年份，如果为0则是未知年份
                val year = if(child.description.extras?.getLong(MediaMetadataCompat.METADATA_KEY_YEAR) != 0L){
                    child.description.extras?.getLong(MediaMetadataCompat.METADATA_KEY_YEAR).toString()
                } else {
                    unknownYear
                }
                AlbumListItem(
                    child.mediaId!!,
                    child.description.title.toString(),
                    child.description.iconUri.toString(),
                    child.description.subtitle.toString(),
                    child.description.subtitle.toString(),
                    child.description.extras?.getString(METADATA_KEY_ALBUM_ID) ?: "-1",
                    child.description.extras?.getString(METADATA_KEY_ARTIST_ID) ?: "-1",
                    year,
                    BaseItemData.LAYOUT_ALBUM_LIST_ITEM_SMALL
                )
            }
            _albumItems.postValue(Resource.Success(itemsList))
        }
    }

    /**
     * 使用协程处理回调的数据（所有歌手内容）
     */
    private suspend fun handleArtistItems(children: List<MediaBrowserCompat.MediaItem>){
        withContext(Dispatchers.Default){
            val itemsList = children.map { child ->
                ArtistListItem(
                    child.mediaId!!,
                    child.description.title.toString(),
                    child.description.iconUri.toString(),
                    ArtistListItem.ArtistSubtitle(child.description.description.toString().toInt(),child.description.subtitle.toString().toInt()),
                    child.description.extras?.getString(METADATA_KEY_ARTIST_ID) ?: "-1"
                )

            }
            _artistItems.postValue(Resource.Success(itemsList))
        }
    }

    /**
     * 如果当前播放内容发生变化，则处理MusicMediaData歌曲项的内容
     * 把正在播放的项的内容更改
     */
    private fun handleMusicItemsChanged(
        mediaMetadata: MediaMetadataCompat
    ): Resource<List<DefaultMusicItem>>{
        val newMediaData = _musicItems.value?.data?.map {
            val useResId = if (it.mediaId == mediaMetadata.id) PLAYING else NO_PLAYING
            it.copy(playbackRes = useResId)
        } ?: emptyList()
        return Resource.Success(newMediaData)
    }

    companion object{
        /**
         * 未处于扫描状态，此时子项可以订阅对应内容
         * 为 RESCAN_NONE 代表当前没有执行重新扫描指令
         */
        const val RESCAN_NONE = -1

        /**
         * 重新扫描本地音乐的状态值
         * 为 RESCAN_LOADING 代表当前正在执行重新扫描指令
         */
        const val RESCAN_LOADING = 0

        /**
         * 扫描完成，防止子项进行重新订阅
         * 为 RESCAN_FINISH 代表当前完成执行重新扫描指令
         */
        const val RESCAN_FINISH = 1
    }

}